package com.swallow.auth.infrastructure.acl.oauth2.support.username;

import com.swallow.auth.infrastructure.acl.oauth2.constants.ErrorConstants;
import com.swallow.auth.infrastructure.acl.oauth2.constants.SecurityConstants;
import com.swallow.auth.infrastructure.acl.oauth2.support.AbstractAuthenticationProvider;
import com.swallow.auth.infrastructure.acl.oauth2.utils.SecurityUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.util.ObjectUtils;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author: yangjie.deng@resico.cn
 * @since: 2024-05-15 10:58:04
 * @version: v1.0.0
 * @describe: 用户名/密码登录处理器
 */
public class UPwdGrantAuthenticationProvider extends AbstractAuthenticationProvider<UPwdGrantAuthenticationToken> {
    @Override
    protected AuthorizationGrantType getAuthorizationGrantType(UPwdGrantAuthenticationToken sourceAuthentication) {
        return sourceAuthentication.getAuthorizationGrantType();
    }

    @Override
    protected Set<String> getAuthorizedScopes(RegisteredClient registeredClient, UPwdGrantAuthenticationToken sourceAuthentication) {
        Set<String> requestScopes = sourceAuthentication.getScopes();
        // Default to configured scopes
        Set<String> authorizedScopes = registeredClient.getScopes();
        if (!ObjectUtils.isEmpty(requestScopes)) {
            Set<String> unauthorizedScopes = requestScopes.stream()
                    .filter(requestedScope -> !registeredClient.getScopes().contains(requestedScope))
                    .collect(Collectors.toSet());

            if (!ObjectUtils.isEmpty(unauthorizedScopes)) {
                SecurityUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST,"OAuth 2.0 Parameter: " + OAuth2ParameterNames.SCOPE);
            }
            authorizedScopes = new LinkedHashSet<>(requestScopes);
        }
        return authorizedScopes;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UPwdGrantAuthenticationToken authenticationToken = (UPwdGrantAuthenticationToken) authentication;

        // 确保客户端已经授权
        OAuth2ClientAuthenticationToken clientPrincipal = SecurityUtils.getAuthenticatedClientElseThrowInvalidClient(authenticationToken);
        // 获取客户端信息
        RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
        // 确保当前请求的授权类型系统是支持的
        if (registeredClient == null || !registeredClient.getAuthorizationGrantTypes().contains(authenticationToken.getAuthorizationGrantType())) {
            SecurityUtils.throwError(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, "客户端未授权");
        }
        // 进行认证,
        Authentication authenticate = getAuthenticatedUser(authenticationToken);

        // 构建access_token
        return doBuildAccessToken(registeredClient, authenticate, authenticationToken, clientPrincipal);
    }

    private Authentication getAuthenticatedUser(UPwdGrantAuthenticationToken authenticationToken) {
        // 获取手机号密码
        Map<String, Object> additionalParameters = authenticationToken.getAdditionalParameters();
        String username = (String) additionalParameters.get(SecurityConstants.U_PWD_USERNAME_PARAM);
        String password = (String) additionalParameters.get(SecurityConstants.U_PWD_PASSWORD_PARAM);

        // 构建UsernamePasswordAuthenticationToken通过AbstractUserDetailsAuthenticationProvider及其子类对用户名密码进行校验
        UsernamePasswordAuthenticationToken unauthenticated = UsernamePasswordAuthenticationToken.unauthenticated(username, password);

        try {
            return getAuthenticationManager().authenticate(unauthenticated);
        } catch (Exception e) {
            String msg;
            if (StringUtils.isNotBlank(e.getMessage()) && StringUtils.equals(ErrorConstants.ERROR_CODE, e.getMessage())) {
                msg = "认证失败: 验证码不正确";
            } else if (StringUtils.isNotBlank(e.getMessage()) && StringUtils.equals(e.getMessage(), ErrorConstants.USER_ACCOUNT_LOCKED)){
                msg = "认证失败: 账号已锁定";
            } else if (StringUtils.isNotBlank(e.getMessage()) && StringUtils.equals(e.getMessage(), ErrorConstants.USER_ACCOUNT_DISABLED)) {
                msg = "认证失败: 账号已禁用";
            } else {
                msg = "认证失败: 用户名密码错误";
            }
            SecurityUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, msg);
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UPwdGrantAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
